package net.xt.testthread.service.impl;

import lombok.extern.slf4j.Slf4j;
import net.xt.testthread.aop.ServiceLock;
import net.xt.testthread.mapper.TestMapper;
import net.xt.testthread.po.TestPO;
import net.xt.testthread.service.ITestService;
import net.xt.testthread.utils.MoreThreadTransactionUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;

@Service
@Slf4j
public class TestServiceImpl implements ITestService {
    @Resource
    private TestMapper testMapper;

    public static List<TestPO> data = data();

    private static List<TestPO> data() {
        int userSize = 10 * 10000;
        List<TestPO> testPOList = new ArrayList<>(userSize);
        for (long i = 1; i <= userSize; i++) {
            testPOList.add(TestPO.builder().id(i).name("用户-" + i).build());
        }
        return testPOList;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void singleThreadInsert() {
        // 分别向 5 张表各插入 10 万条数据
        this.testMapper.batchInsert("t_test_1", data);
        this.testMapper.batchInsert("t_test_2", data);
        this.testMapper.batchInsert("t_test_3", data);
        this.testMapper.batchInsert("t_test_4", data);
        this.testMapper.batchInsert("t_test_5", data);
    }


    @Resource
    private PlatformTransactionManager platformTransactionManager;

    @Override
    public void moreThreadInsert() {
        // 事务多线程事务，向 5 张表插入 10 万条数据
//        boolean success = MoreThreadTransactionUtils.execute(platformTransactionManager,
//                () -> this.testMapper.batchInsert("t_test_1", data),
//                () -> this.testMapper.batchInsert("t_test_2", data),
//                () -> this.testMapper.batchInsert("t_test_3", data),
//                () -> this.testMapper.batchInsert("t_test_4", data),
//                () -> this.testMapper.batchInsert("t_test_5", data));

        boolean success = MoreThreadTransactionUtils.execute2(platformTransactionManager,
                () -> this.testMapper.batchInsert2("t_test_1", data),
                () -> this.testMapper.batchInsert2("t_test_2", data),
                () -> this.testMapper.batchInsert2("t_test_3", data),
                () -> this.testMapper.batchInsert2("t_test_4", data),
                () -> this.testMapper.batchInsert2("t_test_5", data));

        // 所有的插入是否都成功了？
        if (success) {
            System.out.println("插入成功");
        } else {
            System.out.println("插入失败");
        }
    }


    @Override
    public void moreThreadInsertFail() {
        // 事务多线程事务，向 5 张表插入 10 万条数据
        boolean success = MoreThreadTransactionUtils.execute(this.platformTransactionManager,
                () -> this.testMapper.batchInsert("t_test_1", data),
                () -> this.testMapper.batchInsert("t_test_2", data),
                () -> this.testMapper.batchInsert("t_test_3", data),
                () -> this.testMapper.batchInsert("t_test_4", data),
                () -> this.testMapper.batchInsert("t_test_5", data));

        //插入第5表的时候，故意使坏手动丢个异常，让多线程事务都回滚
//                () -> {
//                    this.testMapper.batchInsert("t_test_5", data);
//                    throw new RuntimeException("手动抛出异常，模拟失败场景");
//                });

        // 所有的插入是否都成功了？
        if (success) {
            System.out.println("插入成功");
        } else {
            System.out.println("插入失败");
        }
    }

    @Override
    public void delete() {
        for (int i = 1; i <= 5; i++) {
            String tableName = "t_test_" + i;
            this.testMapper.delete(tableName);
        }
    }

    // 使用自定义注解ServiceLock，防止多线程并发商品数量超卖
    @ServiceLock
    @Override
    public void updateStoreinNum(Integer goodsId) throws Exception {
        Integer storeinNum = this.testMapper.getStoreinNum(goodsId);

        if (storeinNum <= 0) {
            throw new Exception("商品已卖完");
        }

        this.testMapper.updateStoreinNum(goodsId);
    }
}
